/**
 * @file fatfs.c
 * @author lcz (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2022-11-11
 *
 * @copyright Copyright (c) 2022
 *
 */

#include "fs/fatfs/fatfs.h"
#include "fs/fs.h"
#include "dev/dev.h"
#include "tools/log.h"
#include "core/memory.h"
#include "tools/kilb.h"
#include "fs/fatfs/fatfs.h"
#include <sys/fcntl.h>
#include "tools/kilb.h"
// #include <sys/stat.h>

file_type_t diritem_get_type(diritem_t *diritem);
static diritem_t *read_dir_entry(fat_t *fat, int index);
int bread_sector(fat_t *fat, int sector);
int bwrite_sector(fat_t *fat, int sector);
int expand_file(file_t *file, int incr_bytes);
/**
 * @brief 从目录项中读取file文件结构的信息并赋值
 * @param fat fat目录
 * @param file file文件结构
 * @param item 目录项
 * @param index 索引
 */
static void read_from_diritem(fat_t *fat, file_t *file, diritem_t *item, int index)
{
    file->type = diritem_get_type(item);
    file->size = item->DIR_FileSize;
    file->pos = 0;
    file->p_index = index;
    // 获取最开始的起始簇号

    file->sblk = (item->DIR_FstClusHI << 16) | (item->DIR_FstClusL0);
    // 当前簇为起始簇
    file->cblk = file->sblk;
}
/**
 * @brief 写目录项item到对应的目录表fat中，索引为index
 * @param fat 目录表
 * @param item 目录项
 * @param index 索引
 */
static int write_dir_entry(fat_t *fat, diritem_t *item, int index)
{
    // 合法性校验
    if ((index < 0) || (index >= fat->root_ent_cnt))
    {
        return -1;
    }
    // 获取在根目录中的字节偏移
    int offset = index * sizeof(diritem_t);
    // 获取目录项在分区中的扇区号
    int sector = fat->root_start + offset / fat->bytes_per_sec;
    // 内部的缓存
    // 读取很多次的磁盘,index对应的目录项在同一个扇区，我们就别读了吧 buff_read
    // 读到fat->fat_buffer
    int err = bread_sector(fat, sector);
    if (err < 0)
    {
        return -1;
    }
    // 拷贝 将内存中初始化好了的item表项写到fat内存缓冲中，后续再写回硬盘
    kernel_memcpy(fat->fat_buffer + offset % fat->bytes_per_sec, item, sizeof(diritem_t));

    // 回写
    return bwrite_sector(fat, sector);
}

/**
 * @brief 输入路径文件名转换成fat16文件名
 * @param dest 目的地址，存放转换后的字符串
 * @param src 源地址，路径文件名
 */
static void to_sfn(char *dest, const char *src)
{
    // 目标位清空
    kernel_memset(dest, ' ', 11);
    char *curr = dest;
    char *end = dest + 11;
    while (*src && (curr < end))
    {
        char c = *src++;
        switch (c)
        {
            // 遇到 '.'文件名称结束，检测后缀
        case '.':
            curr = dest + 8;
            break;
        default:
            // 小写变大写
            if ((c >= 'a') && (c <= 'z'))
            {
                c = c - 'a' + 'A';
            }
            *curr++ = c;
            break;
        }
    }
}
/**
 * @brief 路径文件名与fat16文件名的比较
 * @param item 目录项（保存fat16文件名）
 * @param path 路径文件名
 * @return * int 同返回1 不同0
 */
int diritem_name_match(diritem_t *item, const char *path)
{
    char buf[SFN_LEN];
    // 将path转换成 diritem_t类型
    // to short file name
    to_sfn(buf, path);
    return kernel_memcmp(buf, item->DIR_Name, 11) == 0;
}
/**
 * @brief 初始化目录项
 * @param item 目录项
 * @param attr 属性
 * @param name 名称
 */
int diritem_init(diritem_t *item, uint8_t attr, const char *name)
{
    to_sfn((char *)item->DIR_Name, name);
    item->DIR_FstClusHI = (uint16_t)(FAT_CLUSTER_INVALID >> 16);
    item->DIR_FstClusL0 = (uint16_t)(FAT_CLUSTER_INVALID & 0xffff);
    item->DIR_Attr = attr;
    item->DIR_FileSize = 0;
    item->DIR_CrtTime = 0;
    item->DIR_CrtDate = 0;
    item->DIR_WrtDate = 0;
    item->DIR_WrtTime = 0;
    item->DIR_LastAccDate = 0;
}

/**
 * @brief 判断簇索引是否合法
 * @param cluster 缩影
 * @return * int
 */
int cluster_is_valid(cluster_t cluster)
{
    // 小于错误值，大于2
    return (cluster < FAT_CLUSTER_INVALID) && (cluster >= 0x2);
}
/**
 * @brief 设置下一个块
 * @param fat 文件目录
 * @param curr 当前的块
 * @param next 下一块
 * @return * int
 */
int cluster_set_next(fat_t *fat, cluster_t curr, cluster_t next)
{
    // 合法性校验
    if (!cluster_is_valid(curr))
    {
        return -1;
    }
    // 很大的数组
    // 获取字节偏移
    int offset = curr * sizeof(cluster_t);
    // 计算当前簇所在扇区
    int sector = offset / fat->bytes_per_sec;
    // 计算当前簇所在扇区偏移
    int off_sector = offset % fat->bytes_per_sec;
    // 扇区过大，一个fat表一个扇区 两个
    if (sector >= fat->tbl_sectors)
    {
        log_printf("cluster too big: %d", curr);
        return FAT_CLUSTER_INVALID;
    }
    // 读取成功 继续读取文件 实际上这里读取的是簇表
    int err = bread_sector(fat, fat->tbl_start + sector);
    if (err < 0)
    {
        return FAT_CLUSTER_INVALID;
    }
    // 获取下一各  fat表项有一个或多个的  清空表项的时候，这里的next值为0哦
    // fat目录中对应的簇表项被设置为next值
    *(cluster_t *)(fat->fat_buffer + off_sector) = next;
    // 对于每个fat目录表 我们都要回写
    for (int i = 0; i < fat->tbl_cnt; i++)
    {
        err = bwrite_sector(fat, fat->tbl_start + sector);
        if (err < 0)
        {
            log_printf("write cluster failed.");
            return -1;
        }
        // 调整扇区号到下一各扇区表中(下一各fat表项中)
        sector += fat->tbl_sectors;
    }

    // 返回下一个簇表的地址
    return 0;
}

/**
 * @brief 获取下一个簇的索引下标
 * @param fat fat目录
 * @param curr 当前的索引下标
 * @return * int
 */
int cluster_get_next(fat_t *fat, cluster_t curr)
{
    // 合法性校验
    if (!cluster_is_valid(curr))
    {
        return FAT_CLUSTER_INVALID;
    }

    // 很大的数组
    // 获取字节偏移
    int offset = curr * sizeof(cluster_t);
    // 计算当前簇所在扇区
    int sector = offset / fat->bytes_per_sec;
    // 计算当前簇所在扇区偏移
    int off_sector = offset % fat->bytes_per_sec;
    // 扇区过大，一个fat表一个扇区 两个
    if (sector >= fat->tbl_sectors)
    {
        log_printf("cluster too big: %d", curr);
        return FAT_CLUSTER_INVALID;
    }
    // 读取成功 继续读取文件 实际上这里读取的是簇表
    int err = bread_sector(fat, fat->tbl_start + sector);
    if (err < 0)
    {
        return FAT_CLUSTER_INVALID;
    }
    // 返回下一个簇表的地址
    return *(cluster_t *)(fat->fat_buffer + off_sector);
}

/**
 * @brief 簇释放
 * @param fat fat文件目录
 * @param start 开始的簇索引
 */
void cluster_free_chain(fat_t *fat, cluster_t start)
{
    // 最后对应一个无效值
    // 簇有效
    while (cluster_is_valid(start))
    {
        // 获取下一各簇索引先
        cluster_t next = cluster_get_next(fat, start);
        // 表项清零
        cluster_set_next(fat, start, CLUSTER_FAT_FREE);
        start = next;
    }
}

/**
 * @brief 移动文件指针位置
 *
 * @param move_bytes 移动的文件指针位数
 * @param expand 是否扩展文件当文件指针越过该簇
 * @return * int -1失败 0成功
 */
static int move_file_pos(file_t *file, fat_t *fat, uint32_t move_bytes, int expand)
{
    uint32_t c_offset = file->pos % fat->cluster_byte_size;

    if (c_offset + move_bytes >= fat->cluster_byte_size)
    {
        // 获取下一个簇的地址
        cluster_t next = cluster_get_next(fat, file->cblk);
        if ((next == FAT_CLUSTER_INVALID) && expand)
        {
            // next 不合法且需要继续加大
            int err = expand_file(file, fat->cluster_byte_size);
            if (err < 0)
            {
                return -1;
            }
            next = cluster_get_next(fat, file->cblk);
            // return -1;
        }
        // 设置新的簇索引，下一读取数据的时候就是读取下一个文件了
        file->cblk = next;
    }
    file->pos += move_bytes;
    return 0;
}
/**
 * @brief 找一个空闲的cluster
 */
cluster_t cluster_alloc_free(fat_t *fat, int cnt)
{
    // 下一个 当前 开始
    cluster_t pre, curr, start;
    // 我们现在计算一下一共有几个簇表项
    int c_total = fat->tbl_sectors * fat->bytes_per_sec / sizeof(cluster_t);
    // 开始，起始簇表项初始化为空
    pre=start = FAT_CLUSTER_INVALID;

    // 现在遍历 遍历fat目录表中的所有文件
    for (curr = 2; cnt && (curr < c_total); curr++)
    {
        // curr 等于一个值时，其下一各簇表项值为空，证明curr使用
        // 我不理解
        cluster_t free = cluster_get_next(fat, curr);
        if (free == CLUSTER_FAT_FREE)
        {
            // 找到了一个新的簇，我们设置一下 后需要将其插入到表项中
            // 记录首个簇
            if (!cluster_is_valid(start))
            {
                // start 无效运行 ，start为初始值，这是第一个申请的簇
                start = curr;
            }
            // start有效 插入到之前的簇 （这不是第一个申请的簇）
            if (cluster_is_valid(pre))
            {
                int err = cluster_set_next(fat, pre, curr);
                // 设置失败就释放
                if (err < 0)
                {
                    cluster_free_chain(fat, start);
                    return FAT_CLUSTER_INVALID;
                }
            }
            // pre指向刚刚新分配的表项
            pre = curr;
            cnt--;
        }
    }
    // 最后一个簇设置为结束值
    if (cnt == 0)
    {
        int err = cluster_set_next(fat, pre, FAT_CLUSTER_INVALID);
        if (err == 0)
        {
            return start;
        }
    }
    cluster_free_chain(fat, start);
    return start;
}

/**
 * @brief 文件扩充
 * @param file 需要扩充的文件
 * @param incr_bytes 扩充的字节数
 * @return * int
 */
int expand_file(file_t *file, int incr_bytes)
{
    // 获取fat
    fat_t *fat = (fat_t *)file->fs->data;
    // 需要扩充的簇数量
    int cluster_cnt;
    // 原来没有簇或簇刚好用完
    if ((file->size == 0) && (file->size % fat->cluster_byte_size == 0))
    {
        // 我们将incr_bytes调整为一簇为单位 向上取整后计算需要扩充的簇数量
        cluster_cnt = up2(incr_bytes, fat->cluster_byte_size) / fat->cluster_byte_size;
    }
    else
    {
        // 末尾的簇空闲的空间
        int cfree = fat->cluster_byte_size - (file->size % fat->cluster_byte_size);
        // 空闲的空间够用，我们不用分配 直接扩充即可
        if (cfree > incr_bytes)
        {
            return 0;
        }
        // 空闲空间不够用，我们看看分配多少个簇 扩充的簇数量
        cluster_cnt = up2(incr_bytes - cfree, fat->cluster_byte_size) / fat->cluster_byte_size;
        // cluster_cnt为0时分配可能有问题，需要修改
        if (cluster_cnt == 0)
        {
            cluster_cnt = 1;
        }
    }

    // if - else 需要分配新簇的情况  我们要分配多少个
    cluster_t start = cluster_alloc_free(fat, cluster_cnt);
    // 扩充失败
    if (!cluster_is_valid(start))
    {
        log_printf("alloc cluster failed, no cluster for file write");
        return -1;
    }
    // 开始块不合理，即是空文件，一个簇也没有，对应上面的if条件
    if (!cluster_is_valid(file->sblk))
    {
        // 空文件，开始簇当前簇相同
        file->sblk = file->cblk = start;
    }
    else
    {
        // 文件已经有数据了 start 就是下一各块
        int err = cluster_set_next(fat, file->cblk, start);
        if (err < 0)
        {
            return -1;
        }
    }
    return 0;
}

/**
 * @brief fat设备挂载
 * 所以挂载的时候就是读入了磁盘中扇区的一些定义文件，fat16读取了扇区的dbr结构进行初始化
 */
int fatfs_mount(struct _fs_t *fs, int major, int minor)
{
    // 打开设备
    int dev_id = dev_open(major, minor, (void *)0);
    if (dev_id < 0)
    {
        log_printf("open disk failed, major: %x, minor: %x", major, minor);
    }
    // 分配页内存
    dbr_t *dbr = (dbr_t *)memory_alloc_page();
    if (!dbr)
    {
        log_printf("mount failed: can't alloc buf");
        goto mount_failed;
    }
    // 我们将分区也作为一个设备，dev_read会读取
    int cnt = dev_read(dev_id, 0, (char *)dbr, 1);
    if (cnt < 1)
    {
        log_printf("read dbr failed");
        goto mount_failed;
    }

    // 读取数据  获取fs中的fat_data的指针，我们要将数据填写到这里了
    // dbr dbr这一页内存，先是作为一扇区的dbr ，后面再作为一页的扇区
    fat_t *fat = &fs->fat_data;
    // 设置缓冲区，初始化完成fat结构后，存放dbr的内存将作为缓冲区
    fat->fat_buffer = (uint8_t *)dbr;
    fat->bytes_per_sec = dbr->BPB_BytsPerSec;
    // fat表项的开始就是保留区的开始
    fat->tbl_start = dbr->BPB_RsvdSecCnt;
    fat->tbl_sectors = dbr->BPB_FATSz16;
    // fat表的个数
    fat->tbl_cnt = dbr->BPB_NumFATs;
    fat->root_ent_cnt = dbr->BPB_RootEntCnt;
    // 每个簇的扇区数
    fat->sec_per_cluster = dbr->BPB_SecPerClus;
    // 获取根目录表
    fat->root_start = fat->tbl_start + fat->tbl_sectors * fat->tbl_cnt;
    // 根目录区是各表，每个32字节 root_ect_cnt 根目录区表项
    fat->data_start = fat->root_start + fat->root_ent_cnt * 32 / SECTOR_SIZE;
    fat->cluster_byte_size = fat->sec_per_cluster * dbr->BPB_BytsPerSec;
    fat->curr_sector = 0;
    fat->fs = fs;
    mutex_init(&fat->mutex);
    fs->mutex=&fat->mutex;
    // fat表不为2这发送错误
    if (fat->tbl_cnt != 2)
    {
        log_printf("fat table error: major: %x, minor: %x", major, minor);
        goto mount_failed;
    }

    // 判断标志信号是否相同
    if (kernel_memcmp(dbr->BS_FileSysType, "FAT16", 5) != 0)
    {
        log_printf("not a fat filesystem: major: %x, minor: %x", major, minor);
        goto mount_failed;
    }

    fs->type = FS_FAT16;
    // 数据区域现在保存fat分区的一些信息，来源于dbr
    fs->data = &fs->fat_data;
    fs->dev_id = dev_id;
    return 0;
mount_failed:
    // 释放内春，关闭设备
    if (dbr)
    {
        memory_free_page((uint32_t)dbr);
    }
    dev_close(dev_id);
    return -1;
}
/**
 * @brief fat卸载函数，关闭设备，释放缓冲区
 */
void fatfs_unmount(struct _fs_t *fs, int major, int minor)
{
    fat_t *fat = (fat_t *)fs->data;
    dev_close(fs->dev_id);
    memory_free_page((uint32_t)fat->fat_buffer);
}

// 所有设备放在设备文件系统中进行管理
/**
 * @brief 打开指定的文件
 */
int fatfs_open(struct _fs_t *fs, const char *path, file_t *file)
{
    fat_t *fat = (fat_t *)fs->data;
    diritem_t *file_item = (diritem_t *)0;
    int p_index = -1;
    // 读取目录项
    for (int i = 0; i < fat->root_ent_cnt; i++)
    {
        diritem_t *item = read_dir_entry(fat, i);
        if (item == (diritem_t *)0)
        {
            // log_printf("")
            return -1;
        }
        if (item->DIR_Name[0] == DIRITEM_NAME_END)
        {
            p_index = i;
            break;
        }

        if (item->DIR_Name[0] == DIRITEM_NAME_END)
        {
            p_index = i;
            continue;
        }

        if (diritem_name_match(item, path))
        {
            file_item = item;
            p_index = i;
            break;
        }
    }
    if (file_item)
    {
        read_from_diritem(fat, file, file_item, p_index);
        // 文件已存在
        if (file->mode & O_TRUNC)
        {
            // 并且O_TRUNC成立，我们不是追加，清空文件后写入
            cluster_free_chain(fat, file->sblk);
            file->cblk = file->sblk = FAT_CLUSTER_INVALID;
            file->size = 0;
        }
        return 0;
    }
    else if ((file->mode & O_CREAT) && (p_index >= 0))
    {
        // 没找到指定的文件，是否创建并打开该文件
        diritem_t item;
        // 初始化内存中的目录项
        diritem_init(&item, 0, path);
        // 将内存的目录项item写入到那个（对应的）索引中
        int err = write_dir_entry(fat, &item, p_index);
        if (err < 0)
        {
            log_printf("create file failed");
        }
        // 将该索引读取出来
        read_from_diritem(fat, file, &item, p_index);
        return 0;
    }else{
        return -1;
    }
}
// 打开的时候传一个file结构，描述一个特定的打开文件,我们将fs_t 的信息放在file结构中
int fatfs_read(char *buf, int size, file_t *file)
{
    fat_t *fat = (fat_t *)file->fs->data;
    uint32_t nbytes = size;
    // nbytes读取的字节量 读到文件的末尾，及文件不够读了
    // 累计读取的数据，文件指针大于等于 文件大小的时候不执行while循环，自接退出，返回0
    // 从而不在继续打印 大概就是这样的
    if (file->pos + nbytes > file->size)
    {
        nbytes = file->size - file->pos;
    }

    uint32_t total_read = 0;
    while (nbytes > 0)
    {
        //当前应该读取的数据量
        uint32_t curr_read = nbytes;
        uint32_t culster_offset = file->pos % fat->cluster_byte_size;
        // 数据区为簇的集合，簇从2开始
        int start_sector = fat->data_start + (file->cblk - 2) * fat->sec_per_cluster;

        // culster_offset不为0，簇的中间开始读，读到这个簇结束
        // 簇的起始位置读，且刚好读取一个簇
        if ((culster_offset == 0) && (nbytes == fat->cluster_byte_size))
        {
            int err = dev_read(fat->fs->dev_id, start_sector, buf, fat->sec_per_cluster);
            if (err < 0)
            {
                return total_read;
            }
            curr_read = fat->cluster_byte_size;
        }
        else
        {
            if (culster_offset + curr_read > fat->cluster_byte_size)
            {
                // 读完当前这个簇
                curr_read = fat->cluster_byte_size - culster_offset;
            }
            // 缓存无效
            fat->curr_sector = -1;
            int err = dev_read(fat->fs->dev_id, start_sector, fat->fat_buffer, fat->sec_per_cluster);
            if (err < 0)
            {
                return total_read;
            }
            // 注意我们仍然读取了一个簇大小的块，但是我么在复制到buf缓冲区的时候fat->buffer+cluster_offset从这里开始复制，且复制的大小为curr_read，因此就是读取了pos开始到该簇结束的大小，后续的簇我们不考虑了
            kernel_memcpy(buf, fat->fat_buffer + culster_offset, curr_read);
        }
        //进行调整
        buf += curr_read;
        nbytes -= curr_read;
        total_read += curr_read;
        // 是否增大文件
        int err = move_file_pos(file, fat, curr_read, 0);
        if (err < 0)
        {
            return total_read;
        }
    }
    return total_read;
}

/**
 * @brief 写文件数据
 */
int fatfs_write(char *buf, int size, file_t *file)
{

    fat_t *fat = (fat_t *)file->fs->data;
    if (file->pos + size > file->size)
    {
        // 超过文件大小 需要扩孔
        int inc_size = file->pos + size - file->size;
        // 需要扩大的字节量inc_size
        int err = expand_file(file, inc_size);
        if (err < 0)
        {
            return 0;
        }
    }
    // 完成扩大后 开始写入
    uint32_t nbytes = size;
    uint32_t total_write = 0;
    // 还需要写入，read write代码有点相识
    while (nbytes)
    {
        uint32_t curr_write = nbytes;
        uint32_t cluster_offset = file->pos % fat->cluster_byte_size;
        uint32_t start_sector = fat->data_start + (file->cblk - 2) * fat->sec_per_cluster; // 从2开始

        // 如果是整簇, 只写一簇 我们刚好写一簇吧
        if ((cluster_offset == 0) && (nbytes == fat->cluster_byte_size))
        {
            int err = dev_write(fat->fs->dev_id, start_sector, buf, fat->sec_per_cluster);
            if (err < 0)
            {
                return total_write;
            }
            // 当前写的一簇的大小
            curr_write = fat->cluster_byte_size;
        }
        else
        {
            // 写入的时候超过1簇，及写入的部分跨簇了
            // 我们要先读入簇再写回去 将簇中的内容先缓存起来
            if (cluster_offset + curr_write > fat->cluster_byte_size)
            {
                // 过一簇，本簇写的大小
                curr_write = fat->cluster_byte_size - cluster_offset;
            }

            // 读取整个簇，然后从中拷贝
            fat->curr_sector = -1;
            int err = dev_read(fat->fs->dev_id, start_sector, fat->fat_buffer, fat->sec_per_cluster);
            if (err < 0)
            {
                return total_write;
            }
            // 进行复制 后写回去
            kernel_memcpy(fat->fat_buffer + cluster_offset, buf, curr_write);
            // 写回去
            err = dev_write(fat->fs->dev_id, start_sector, fat->fat_buffer, fat->sec_per_cluster);
            if (err < 0)
            {
                return total_write;
            }
        }
        buf += curr_write;
        nbytes -= curr_write;
        total_write += curr_write;

        // 文件大大小设置，只能是从无到有 只能做到每次写都视为创建新文件
        file->size += curr_write;

        // 前移文件指针
        int err = move_file_pos(file, fat, curr_write, 1);
        if (err < 0)
        {
            return total_write;
        }
    }
    return total_write;
}
/**
 * @brief 关闭文件
 */
void fatfs_close(file_t *file)
{
    // 文件以只读的形式打开的话
    if (file->mode == O_RDONLY)
    {
        return;
    }

    fat_t *fat = (fat_t *)file->fs->data;
    // 读取目录
    diritem_t *item = read_dir_entry(fat, file->p_index);
    if (item == (diritem_t *)0)
    {
        return;
    }
    item->DIR_FileSize = file->size;
    item->DIR_FstClusHI = (uint16_t)(file->sblk >> 16);
    item->DIR_FstClusL0 = (uint16_t)(file->sblk & 0xFFFF);
    write_dir_entry(fat, item, file->p_index);
}
/**
 * @brief 文件读写位置的调整
 * dir 0：开头  1：当前位置	2：末尾
 */
int fatfs_seek(file_t *file, uint32_t offset, int dir)
{
    // 仅支持0模式 ， 且我们不仅要调整pos且要调整当前簇
    if (dir != 0)
    {
        return -1;
    }
    // 设置相应变量
    fat_t *fat = file->fs->data;
    cluster_t current_cluster = file->cblk;
    uint32_t curr_pos = 0;
    uint32_t offset_to_move = offset;
    // 还需要移动，offset_to_move还不等于0
    while (offset_to_move)
    {
        // 当前需要移动的量 因为要搭配while循环，所以必须这么写
        uint32_t c_offset = curr_pos % fat->cluster_byte_size;
        uint32_t curr_move = offset_to_move;
        // 不超过一簇，直接调整位置，无需跑到下一簇
        if (c_offset + curr_move < fat->cluster_byte_size)
        {
            curr_pos += curr_move;
            break;
        }
        // 一次调整太大，该小一点
        curr_move = fat->cluster_byte_size - c_offset;
        curr_pos += curr_move;
        offset_to_move -= curr_move;
        // 获取下一结点的簇的位置并进行设置
        current_cluster = cluster_get_next(fat, current_cluster);
        if (!cluster_is_valid(current_cluster))
        {
            return -1;
        }
    }
    // pos 文件指针更新完毕，设置新的文件指针
    // 调整当前的簇
    file->pos = curr_pos;
    file->cblk = current_cluster;
}
int fatfs_stat(file_t *file, struct stat *st)
{
    return -1;
}
/**
 * @brief 打开目录。只是简单地读取位置重设为0
 */
int fatfs_opendir(struct _fs_t *fs, const char *name, DIR *dir)
{
    dir->index = 0;
    return 0;
}

/**
 * @brief 获取文件类型，目前仅支持普通文件及目录文件
 * 部分目录项会作为长文件名 要检测一下
 * @param diritem 目录项
 * @return * file_type_t 文件类型
 */
file_type_t diritem_get_type(diritem_t *diritem)
{
    file_type_t type = FILE_UNKNOWN;
    // 文件类型不行
    if (diritem->DIR_Attr & (DIRITEM_ATTR_VOLUME_ID | DIRITEM_ATTR_HIDDEN | DIRITEM_ATTR_SYSTEM))
    {
        return type;
    }
    // 长文件名
    // if ((diritem->DIR_Attr & DIRITEM_ATTR_LONG_NAME) == DIRITEM_ATTR_LONG_NAME)
    // {
    //     return type;
    // }
    // 判断文件类型
    type = diritem->DIR_Attr & DIRITEM_ATTR_DIRECTORY ? FILE_DIR : FILE_NORMAL;
    return type;
}
/**
 * @brief fat结构写回扇区
 * @param fat fat目录表
 * @param sector 扇区号
 */
int bwrite_sector(fat_t *fat, int sector)
{
    int cnt = dev_write(fat->fs->dev_id, sector, fat->fat_buffer, 1);
    return (cnt == 1) ? 0 : -1;
}

// fat_t add int curr_sector;     // 当前缓存的扇区数
// fatfs_mount 中初始化
/**
 * @brief 带缓存的读
 * @param fat fat结构
 * @param sector 读取的扇区索引
 * @return * int 正确0 错误-1
 */
int bread_sector(fat_t *fat, int sector)
{
    // 如果已经前一次读取的扇区同现在为同一个扇区，则在缓冲中取数据即可，不用再读了
    if (sector == fat->curr_sector)
    {
        return 0;
    }

    int cnt = dev_read(fat->fs->dev_id, sector, fat->fat_buffer, 1);
    if (cnt == 1)
    {
        // 设置 curr_sector
        fat->curr_sector = sector;
        return 0;
    }
    return -1;
}

/**
 * @brief 读取目录项
 * @param fat fat结构
 * @param index 目录项索引
 * @return * diritem_t* 放回目录项
 */
static diritem_t *read_dir_entry(fat_t *fat, int index)
{
    // 合法性校验
    if ((index < 0) || (index >= fat->root_ent_cnt))
    {
        return (diritem_t *)0;
    }
    // 获取在根目录中的字节偏移
    int offset = index * sizeof(diritem_t);
    // 获取目录项在分区中的扇区号
    int sector = fat->root_start + offset / fat->bytes_per_sec;
    // 内部的缓存
    // 读取很多次的磁盘,index对应的目录项在同一个扇区，我们就别读了吧 buff_read
    int err = bread_sector(fat, sector);
    if (err < 0)
    {
        return (diritem_t *)0;
    }
    // 偏移对扇区大小取余数，得到扇区内的偏移，读取的buffer起始地址为目标扇区的起始地址
    // 加上余数救赎目标diritem_t结构的起始地址
    return (diritem_t *)(fat->fat_buffer + offset % fat->bytes_per_sec);
}
/**
 * @brief 获取名称
 * @param item 目录项
 * @param dest 目标地址
 */
void diritem_get_name(diritem_t *item, char *dest)
{
    char *c = dest;
    char *ext = (char *)0;
    // 放名称的地址先清空 '\0'
    kernel_memset(dest, 0, 12);
    for (int i = 0; i < 11; i++)
    {
        // 获取名称
        if (item->DIR_Name[i] != ' ')
        {
            *c++ = item->DIR_Name[i];
        }
        // 判断是否加后缀，有则加点
        if (i == 7)
        {
            // 先看一下扩展名
            ext = c;
            *c++ = '.';
        }
    }
    // 没有扩展名的情况
    if (ext && (ext[1] == '\0'))
    {
        ext[0] = '\0';
    }
}

/**
 * @brief fat16文件系统读取根目录
 * @param fs 文件系统
 * @param dir 目录
 * @param dirent 目录项
 * @return * int 正确返回0 错误-1
 */
int fatfs_readdir(struct _fs_t *fs, DIR *dir, struct _dirent *dirent)
{
    // int flags = 0;
    fat_t *fat = (fat_t *)fs->data;
    // 读取目录项 读到根目录项完为止
    while (dir->index < fat->root_ent_cnt)
    {
        // flags=0;
        // 读取目录项
        diritem_t *item = read_dir_entry(fat, dir->index);
        // 不是所有的item都有目录项存在
        if (item == (diritem_t *)0)
        {
            return -1;
        }
        // 0xe5跳过 0x00结尾标记
        if (item->DIR_Name[0] == DIRITEM_NAME_END)
        {
            // return -1;
            break;
        }
        // 找到一个有效的目录项
        // flags=1;
        if (item->DIR_Name[0] != DIRITEM_NAME_FREE)
        {
            // 获取类型
            file_type_t type = diritem_get_type(item);
            // 类型正确
            if ((type == FILE_DIR) || (type == FILE_NORMAL))
            {
                // 设置类型 大小 名称及索引自增
                dirent->size = item->DIR_FileSize;
                dirent->type = type;
                // 获取名称
                diritem_get_name(item, dirent->name);
                // dirent->index为未加1前的值
                dirent->index = dir->index++;
                return 0;
            }
        }
        dir->index++;
    }
    // if (flags)
    // {
    //     return 0;
    // }
    // else
    // {
    return -1;
    // }
}
/**
 * @brief 关闭文件扫描读取
 */
int fatfs_closedir(struct _fs_t *fs, DIR *fir)
{
    return 0;
}
/**
 * @brief 清除文件连接目录表，我们将清除簇后清除目录
 * @param path 文件路径，默认文件系统为跟文件系统
 */
int fatfs_unlink(struct _fs_t *fs, const char *path)
{
    fat_t *fat = (fat_t *)fs->data;
    // 读取目录项
    for (int i = 0; i < fat->root_ent_cnt; i++)
    {
        diritem_t *item = read_dir_entry(fat, i);
        if (item == (diritem_t *)0)
        {
            return -1;
        }
        if (item->DIR_Name[0] == DIRITEM_NAME_END)
        {
            break;
        }
        if (item->DIR_Name[0] == DIRITEM_NAME_END)
        {
            continue;
        }

        if (diritem_name_match(item, path))
        {
            // 找到了对应表项
            // 找到匹配的目录
            int cluster = (item->DIR_FstClusHI << 16) | item->DIR_FstClusL0;
            cluster_free_chain(fat, cluster);
            diritem_t item;
            // 表项清零
            kernel_memset(&item, 0, sizeof(diritem_t));
            // 写回当前表项
            return write_dir_entry(fat, &item, i);
        }
    }
    // 没有找到表项
    return -1;
}

// fat文件系统的回调函数
fs_op_t fatfs_op = {
    .mount = fatfs_mount,
    .unmount = fatfs_unmount,
    .open = fatfs_open,
    .write = fatfs_write,
    .close = fatfs_close,
    .seek = fatfs_seek,
    .stat = fatfs_stat,
    .read = fatfs_read,
    .opendir = fatfs_opendir,
    .readdir = fatfs_readdir,
    .closedir = fatfs_closedir,
    // .ioctl = fatfs_ioctl,
    .unlink = fatfs_unlink,
};